package sk.uniza.fri.cerviix.server.game;

import java.util.LinkedList;
import java.util.List;

public class QuadTree
{
	private Node node;

	public QuadTree(float fromX, float toX, float fromY, float toY)
	{
		node = new Node(fromX, toX, fromY, toY);
	}

	public void addWormPart(WormPart part)
	{
		node.addWormPart(part);
	}

	public List<WormPart> getPartsInsideBox(float xFrom, float xTo,
			float yFrom, float yTo)
	{
		List<WormPart> wormParts = new LinkedList<WormPart>();

		return node.getWormParts(wormParts, new Box(xFrom, xTo, yFrom, yTo));
	}

	private class Node
	{
		public float fromX, toX, fromY, toY;

		public WormPart part = null;
		public Node[] nodes = null;

		public Node(float fromX, float toX, float fromY, float toY)
		{
			this.fromX = fromX;
			this.toX = toX;
			this.fromY = fromY;
			this.toY = toY;
		}

		public void addWormPart(WormPart part)
		{
			if (this.part == null && nodes == null)
			{
				this.part = part;
			}
			else
			{
				if (nodes == null)
				{
					createNodes();
				}

				Node node = findNode(part.x, part.y);

				if (node == null)
				{
					throw new RuntimeException("Terrible, terrible damaged QuadTree - can't find Node to add WormPart.");
				}

				node.addWormPart(part);

				if (this.part != null)
				{
					node = findNode(this.part.x, this.part.y);

					if (node == null)
					{
						throw new RuntimeException(
								"Terrible, terrible damaged QuadTree - can't find Node to add WormPart.");
					}

					node.addWormPart(this.part);
					this.part = null;
				}

			}
		}

		private void createNodes()
		{
			float centerX = calcCenter(fromX, toX);
			float centerY = calcCenter(fromY, toY);

			nodes = new Node[4];
			
			nodes[0] = new Node(fromX, centerX, fromY, centerY);
			nodes[1] = new Node(fromX, centerX, centerY, toY);
			nodes[2] = new Node(centerX, toX, fromY, centerY);
			nodes[3] = new Node(centerX, toX, centerY, toY);
		}

		private float calcCenter(float from, float to)
		{
			float center = (to - from) / 2;

			return from + center;
		}

		private Node findNode(float x, float y)
		{
			for (Node node : nodes)
			{
				if (x >= node.fromX && x <= node.toX && y >= node.fromY && y <= node.toY)
				{
					return node;
				}
			}

			return null;
		}

		public List<WormPart> getWormParts(List<WormPart> collector, Box box)
		{
			if (part != null)
			{
				collector.add(part);
			}
			else
			{
				if (nodes != null)
				{
					for (Node node : nodes)
					{
						if (node.isCollidingBox(box))
						{
							node.getWormParts(collector, box);
						}
					}
				}
			}

			return collector;
		}

		public boolean isCollidingBox(Box box)
		{
			if (fromX > box.toX)
			{
				return false;
			}

			if (toX < box.fromX)
			{
				return false;
			}

			if (fromY > box.toY)
			{
				return false;
			}

			if (toY < box.fromY)
			{
				return false;
			}

			return true;
		}
	}

	private class Box
	{
		public final float fromX, toX, fromY, toY;

		public Box(float fromX, float toX, float fromY, float toY)
		{
			this.fromX = fromX;
			this.toX = toX;
			this.fromY = fromY;
			this.toY = toY;
		}

		public boolean isPointInside(float x, float y)
		{
			return (x >= fromX && x <= toX && y >= fromY && y <= toY) ? true
					: false;
		}
	}
}
